﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Meraz.Core.Commands;
using Meraz.Core.Model;
using Meraz.Core.Parsers;
using Meraz.Core.Repositories;
using Ninject;
using System.IO;
using Meraz.Core.Helpers;
using Meraz.Core.Factories;
using Meraz.Core.Config;
using System.Xml.Linq;
namespace Meraz.Core.Managers
{
    public class TwitterManager
    {
        private delegate void UploadStream(Stream stream);
        private delegate void LoadTweet(TweetValues values);
        #region Properties
        [Inject]
        public ICreateTwitterUserCommand CreateTwitterUserCommand { get; set; }
        [Inject]
        public ICreateTweetCommand CreateTweetCommand { get; set; }

        [Inject]
        public ICreateDomainsCommand CreateDomainsCommand { get; set; }

        [Inject]
        public ICreateUrlsCommand CreateUrlsCommand { get; set; }

        [Inject]
        public ITweetValuesParser TwitterParser { get; set; }

        [Inject]
        public ITweetRepository TweetRepository { get; set; }

        [Inject]
        public ITwitterTagRepository TwitterTagRepository { get; set; }

        [Inject]
        public ITwitterMentionRepository TwitterMentionRepository { get; set; }

        [Inject]
        public ITwitterUserRepository TwitterUserRepository { get; set; }

        [Inject]
        public ITwitterLinkRepository TwitterLinkRepository { get; set; }

        [Inject]
        public ITwitterStudyRepository TwitterStudyRepository { get; set; }

        [Inject]
        public ISaveTweetCommand SaveTweetCommand { get; set; }

        [Inject]
        public ISaveTwitterUserCommand SaveTwitterUserCommand { get; set; }

        [Inject]
        public ICreateUrlRedirectCommand CreateUrlRedirectCommand { get; set; }

        [Inject]
        public INetworkHelper NetworkHelper { get; set; }

        [Inject]
        public IObjectFactory ObjectFactory { get; set; }
        #endregion

        public TwitterUser AddUser(string username)
        {
            return CreateTwitterUserCommand.Execute(0, username);
        }
        public TwitterUser AddUser(string username, int studyID)
        {
            IAddTwitterUserCommand command = ObjectFactory.GetAddTwitterUserCommand(studyID, username);
            command.Execute();
            return command.Result;
        }
        public TwitterUser AddUser(string username, int studyID, TwitterSection twitterSection)
        {
            ICreateTwitterUserCommand createUserCommand =
                ObjectFactory.GetCreateTwitterUserCommand();
            TwitterUser twitterUser = createUserCommand.Execute(0, username);
            IAddTwitterUserCommand command = ObjectFactory.GetAddTwitterUserCommand(studyID, username);
            command.Execute();
            twitterSection.Timeline.EndDate = GetStudy(studyID).EndDate;
            Load(twitterSection, username);
            return command.Result;
        }
        public TwitterStudy AddStudy(string studyName)
        {
            ISaveTwitterStudyCommand command = ObjectFactory.GetSaveTwitterStudyCommand(studyName);
            command.Execute();
            return command.Study;
        }
        public TwitterStudy GetStudy(int studyID)
        {
            TwitterStudy twitterStudy = TwitterStudyRepository.GetById(studyID);
            return twitterStudy;
        }
        public List<TwitterStudy> GetStudies()
        {
            return TwitterStudyRepository.All();
        }

        public Tweet GetTweet(long tweetID)
        {
            if (tweetID <= 0)
                throw new ApplicationException("Invalid tweet ID");
            Tweet tweet = TweetRepository.GetById(tweetID);
            if (tweet == null)
                throw new ApplicationException("Tweet record was not found");
            return tweet;
        }
        public TwitterUser GetTweets(string username)
        {
            TwitterUser user = TwitterUserRepository.GetByUsername(username);
            if (user != null)
                user.Tweets = TweetRepository.GetByUserID(user.ID);
            return user;
        }
        public TwitterUser GetTweets(string username, int studyID)
        {
            TwitterStudy study = TwitterStudyRepository.GetById(studyID);

            TwitterUser user = TwitterUserRepository.GetByUsername(username);
            if (user != null)
                user.Tweets = TweetRepository.GetByUserID(user.ID);
            //unit testing proved this doesn't work
            //user.Tweets.RemoveAll(remove1 => (remove1.Pubdate < study.StartDate && remove1.Pubdate > study.EndDate));
            user.Tweets.RemoveAll(remove1 => !remove1.InRange(study.StartDate, study.EndDate));
            return user;
        }

        public TwitterUser GetTwitterUser(long userID)
        {
            return TwitterUserRepository.GetById(userID);
        }
        public List<TwitterUser> GetTwitterUsers()
        {
            return TwitterUserRepository.All();
        }

        public void Load(TwitterSection twitterSection, string twitterUsername)
        {
            TwitterXmlParser parser = new TwitterXmlParser();
            if (string.IsNullOrWhiteSpace(twitterUsername))
                return;

            ICreateTwitterUserCommand userCommand = ObjectFactory.GetCreateTwitterUserCommand();
            userCommand.Execute(0, twitterUsername);
            bool reload = false;
            int pageNumber = 0;
            do
            {
                string url = twitterSection.Timeline.GetUrl(++pageNumber, twitterUsername);
                IEnumerable<XElement> elements = parser.GetElementList(url);
                List<TweetValues> statuses = parser.GetStatuses(elements);
                foreach (TweetValues status in statuses)
                {
                    status.Username = twitterUsername;
                    //Parse Mentions, Tags, Urls
                    TwitterParser.ParseClient(status);
                    ICreateTwitterClientCommand clientCommand = ObjectFactory.GetCreateTwitterClientCommand(status.ClientName);
                    clientCommand.Execute();
                    status.Client = clientCommand.Result;

                    ICreateTweetCommand command = ObjectFactory.GetCreateTweetCommand(status);
                    command.Execute();
                    reload = (twitterSection.Timeline.EndDate < status.Pubdate);
                }
            } while (reload);
        }

        public void Load(TweetValues values)
        {
            //Parse Mentions, Tags, Urls
            TwitterParser.ParseClient(values);
            ICreateTwitterClientCommand clientCommand = ObjectFactory.GetCreateTwitterClientCommand(values.ClientName);
            clientCommand.Execute();
            values.Client = clientCommand.Result;

            ICreateTwitterUserCommand userCommand = ObjectFactory.GetCreateTwitterUserCommand();
            userCommand.Execute(values.UserID, values.Username);

            ICreateTweetCommand command = ObjectFactory.GetCreateTweetCommand(values);
            command.Execute();
        }


        public bool RemoveUser(string username)
        {
            IRemoveTwitterUserCommand command = ObjectFactory.GetRemoveTwitterUserCommand(username);
            command.Execute();
            return command.Result;
        }
        public bool RemoveUser(string username, int studyID)
        {
            IRemoveTwitterUserCommand command = ObjectFactory.GetRemoveTwitterUserCommand(username, studyID);
            command.Execute();
            return command.Result;
        }

        public bool RemoveStudy(int studyID)
        {
            IRemoveTwitterStudyCommand command = ObjectFactory.GetRemoveTwitterStudyCommand(studyID);
            command.Execute();
            return command.Result;
        }

        public bool Save(TwitterStudy study)
        {
            ISaveTwitterStudyCommand command = ObjectFactory.GetSaveTwitterStudyCommand(study.Name);
            command.Study = study;
            command.Execute();
            //todo: unhardcode
            return true;
        }

        public Tweet Save(Tweet tweet)
        {
            if (tweet.StatusType == StatusType.Undefined)
                tweet.StatusType = StatusType.Active;

            return SaveTweetCommand.Execute(tweet);
        }
        public TwitterUser Save(TwitterUser user)
        {
            ICommand<TwitterUser> command = ObjectFactory.GetSaveTwitterUserCommand(user);
            command.Execute();
            return command.Result;
        }
        private void CallBack(IAsyncResult ar)
        {
            //AsyncState is set by passing in the delegate to the BeginInvoke’s method. 
            Action<int> t = ar.AsyncState as Action<int>; 
            t.EndInvoke(ar);//calling EndInvoke - REQUIRED 
        }
        public void Upload(string fileName)
        {
            string filePath = Path.Combine(ConfigHelper.TwitterUploadDirectory, fileName);
            FileStream stream = null;
            try
            {
                stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                Upload(stream);
                //UploadStream uploader = new UploadStream(Upload);
                //uploader.BeginInvoke(stream, new AsyncCallback(CallBack), null);
            }
            catch (FileNotFoundException)
            {
                Debug.WriteLine("Bad filePath!");
            }
            finally
            {
                if (stream != null)
                {
                    try
                    {
                        stream.Close();
                    }
                    catch
                    {
                        //already closed    
                    }
                }
            }
        }
        public void Upload(Stream stream)
        {
            using (StreamReader reader = new StreamReader(stream))
            {
                string[] stringSeparators = new String[] { "\t" };
                string line;
                int count = 0;
                while ((line = reader.ReadLine()) != null)
                {
                    try
                    {
                        count++;
                        string[] tokens = line.Split(stringSeparators, StringSplitOptions.None);
                        TweetValues values = new TweetValues();
                        values.SetUserID(tokens[0]);
                        //values.Username = tokens[1];
                        values.SetTweetID(tokens[2]);
                        //values.TweetUsername = tokens[3];
                        values.Published = tokens[4];
                        values.SetPubdate(tokens[4]);
                        values.ClientName = tokens[5];
                        values.Text = tokens[6];

                        //LoadTweet loader = new LoadTweet(Load);
                        //loader.BeginInvoke(values, new AsyncCallback(CallBack), null);
                        Load(values);
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(string.Format("Failed on line {0}: '{0}'", count, line));
                        Debug.WriteLine(string.Format("Exception: {0}", ex));
                        reader.Close();
                        break;
                    }
                }
                reader.Close();
                Debug.WriteLine(string.Format("Parsed {0} lines", count));
            }
            if (stream != null)
            {
                try
                {
                    stream.Close();
                }
                catch
                {
                    //already closed    
                }
            }
        }
    }
}
